#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2009 Iván G. Campaña N. (e-van) <ivan.campana at gmail.com>
# Released under GNU General Public License

__author__ = "ivan.campana@gmail.com"
__version__ = (1,0,0)

## just for debug
from pprint import pprint

import gtk
import gobject
#import gtkhtml2
import pango
import ezGlade

import logging

import subprocess, os
import user
import datetime, time
from threading import Thread
import sqlite3
import gettext

import twitter
import pynotify
import tweet_browser

APP_NAME = "TweetCream"
PROJECT_PATH = os.path.realpath( os.path.dirname(__file__) )
HOME_PATH = os.path.join(user.home, "." + APP_NAME)
CACHE_PATH = os.path.join(HOME_PATH, "cache")
DB_PATH = ""
LOCALE_DIR = os.path.join(PROJECT_PATH, 'locale')

BG_UNREAD = "#FFCC00"
BG_READ = "#FFFFFF"

#Establishes the current language and translation
#locale.setlocale(locale.LC_ALL, 'C')

# Esto permite traducir los textos escritos en el .py (no en glade)
gettext.textdomain(APP_NAME)
gettext.bindtextdomain(APP_NAME, LOCALE_DIR)

# Y las siguientes 2 lineas permiten traducir los textos del Glade
gtk.glade.textdomain(APP_NAME)
gtk.glade.bindtextdomain(APP_NAME, LOCALE_DIR)



# Little hack for translations, just in case
_ = gettext.gettext


def on_url(object, url):
	"Function called when the mouse hovers over a link"
	global mainWindow
	
	if not url:
		return
	splitted_url = url.split(":")
	
	prefix = splitted_url[0]
	
	if prefix == "nick":
		mainWindow.set_status_message(_("Reply to: ") + splitted_url[1], "info")
	elif prefix == "hash":
		mainWindow.set_status_message(_("Write using this tag: ") + splitted_url[1], "info")
	elif prefix == "profile":
		mainWindow.set_status_message(_("View %s profile") % splitted_url[1], "info")
	elif prefix == "retweet":
		mainWindow.set_status_message(_("Retweet this message"), "info")
	else:
		mainWindow.set_status_message(_("Visit this URL: ") + url, "info")
	
def view_profile(window, username):
	global mainWindow
	
	window.set_parent(mainWindow)
	window.set_user(username)
	window.show()
		
def link_clicked(document, link):
	logging.debug('link_clicked: %s' % link)

	global mainWindow
	
	splitted_url = link.split(":")
	prefix = splitted_url[0]
	
	if prefix == "nick" or prefix == "hash":
		# TODO: Make it deactive automatically after sending a message if it was originally hidden
		mainWindow.tbMessage.set_active(True)
		
		new_text = mainWindow.txtMessage.get_text() + link[5:]
		mainWindow.txtMessage.set_text(new_text)
		return
	
	if prefix == "profile":
		profile = wndProfile()
		ezGlade.threaded_call(view_profile, (profile, splitted_url[1]), breakpoint = (lambda: profile.is_loaded))
		
		return
	
	if prefix == "retweet":
		
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		cursor = DBCON.cursor()
		
		cursor.execute("SELECT message FROM tweet where id = ?", (splitted_url[1],))
		row = cursor.fetchone()
		if not row:
			cursor.execute("SELECT message FROM mention where id = ?", (splitted_url[1],))
			row = cursor.fetchone()
			
			if not row:
				cursor.execute("SELECT message FROM direct where id = ?", (splitted_url[1],))
				row = cursor.fetchone()
				
			
		if row:
			mainWindow.tbMessage.set_active(True)
			new_text = "RT @%s: %s" % (splitted_url[2], row[0])
			mainWindow.txtMessage.set_text(new_text)
		return
	
	if prefix == "dm":
		user = splitted_url[1]
		send_dm = wndDM()
		send_dm.set_parent(mainWindow)
		send_dm.set_modal(True)
		send_dm.enUsername.set_text(user)
		send_dm.show()
		return
	
	if prefix =="http":	
		if os.name == 'mac':
			subprocess.call(('open', link))
		elif os.name == 'nt':
			subprocess.call(('start', link))
		elif os.name == 'posix':
			subprocess.call(('xdg-open', link))

def seconds_to_human(delta):
	"Function to convert seconds to a human readable form"
	
	#This function is not precise as it is not using decimals, but it is not relevant at this time
	
	if delta.seconds <= 60:
		return "%s seconds" % delta.seconds
	
	#if more than a day
	days = ""
	if delta.days > 0:
		if delta.days == 1:
			days = "%d day" % delta.days
		else:
			days = "%d days" % delta.days
			
		return days
		
	if delta.seconds >= 3600:
		hours = delta.seconds / 3600
		if hours <= 1 :
			return "%s hour" % (hours)
		else:
			return "%s hours" % (hours)
	else:
		minutes = (delta.seconds / 60)
		return "%s minutes" % (minutes)

def format_message(username, text, date_created = None):
	"Takes the original message details and formats it acordingly to be easily shown"
	
	if date_created:
		date = datetime.datetime.fromtimestamp(date_created)
		now = datetime.datetime.now()
		delta = now - date
		date_created = '%s ago' % seconds_to_human(delta)
		
	else:
		date_created = ""
	
	#controls += """<a href="retweet:%s:%s" class="controls">%s</a> |""" % (id, username, _("Re-Tweet"))
	#controls += """<a href="nick:@%s" class="controls">%s</a> |""" % (username, _("Reply") )
	#controls += """<a href="dm:%s" class="controls">%s</a> |""" % (username, _("DM"))

	message  = "<b>%s</b>\n" % username 
	message += tweet_browser.find_elements( text )
	message += "\n<small><i>%s</i></small>" % date_created
	
	return message

class wndAbout(ezGlade.BaseWindow):
	"About window"
	
	def on_wndAbout_response(self, widget, response):
		self.destroy()
		
	def on_wndAbout_close(self, widget):
		self.destroy()

class mnuPopup(ezGlade.BaseWindow):
	
	def on_mnuExit_activate(self, widget):
		global mainWindow
		
		mainWindow.exit()

class TweetTree(gtk.TreeView):
	_store = None
	_cursor_hand = gtk.gdk.Cursor(gtk.gdk.HAND2)
	__gsignals__ = {
		"user-selected" : (gobject.SIGNAL_RUN_LAST,
								   gobject.TYPE_NONE, 
								   (str, str, ),
								  ),
	}
	
	def __init__(self):
		super(TweetTree, self).__init__()
		self._store = gtk.ListStore(gobject.TYPE_DOUBLE, #User ID
									gtk.gdk.Pixbuf,		#User Picture
									gobject.TYPE_STRING, # Messag
									gobject.TYPE_STRING, #Username
									gobject.TYPE_STRING #Background Color
									)
		
		gtk.TreeView.__init__(self)
		#self.set_size_request(500, 350)
		self.set_headers_visible(False)
		self.set_model(self._store)
		
		pict_column = gtk.TreeViewColumn('Picture')
		
		main_column = gtk.TreeViewColumn('Tweet')
		main_column.set_sizing( gtk.TREE_VIEW_COLUMN_FIXED)

		#
#		main_column.set_property("sizing", gtk.TREE_VIEW_COLUMN_AUTOSIZE)
		
		cell_pict = gtk.CellRendererPixbuf()
		cell_pict.set_property("width", 48)
		cell_pict.set_property("mode", gtk.CELL_RENDERER_MODE_ACTIVATABLE)
		
		#pict_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
		pict_column.pack_start(cell_pict, expand=False)
		pict_column.add_attribute(cell_pict, 'pixbuf', 1)
		
		cell_text = gtk.CellRendererText()
		#cell_text.set_property("editable", True)
		#cell_text.set_property("editable-set", True)
		cell_text.set_property("family", "Sans")
		cell_text.set_property("size-points", 7)
		cell_text.set_property("mode", gtk.CELL_RENDERER_MODE_ACTIVATABLE)
		
		#cell_text.set_property("single-paragraph-mode", True)
		cell_text.set_property("wrap-mode", pango.WRAP_WORD)
		cell_text.set_property("wrap-width", 300)
		
		main_column.pack_end(cell_text, expand=True)
		main_column.add_attribute(cell_text, 'markup', 2)
		
		
		main_column.add_attribute(cell_text, "background", 4)
		
		self.append_column(pict_column)
		self.append_column(main_column)
		
		self.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
		self.set_hover_selection(True)
		
		self.connect("user-selected", self._on_user_selected)
		self.connect("motion-notify-event", self._on_motion_notify_event)
		self.connect("cursor-changed", self._on_cursor_changed)
		self.connect("button-press-event", self._on_button_press_event)
		#"odd-row-color"
		#self.style.set_property("even-row-color", gtk.gdk.Color("#FFCC00"))
		self.get_selection().set_mode(gtk.SELECTION_BROWSE)
		self.show()
		
	def _on_cursor_changed(self, treeview):
		selection = treeview.get_selection()
		(model, iter) = selection.get_selected()
		if iter is None:
			return
		
		#print model[iter][0]
		#(name, text, icon, overlay, pkgname) = model[iter]
	
	def _on_button_press_event(self, widget, event):
		if event.button != 1:
			return
		res = self.get_path_at_pos(int(event.x), int(event.y))
		if not res:
			return
		(path, column, wx, wy) = res
		if path is None:
			return
		# only act when the selection is already there 
		selection = widget.get_selection()
		if not selection.path_is_selected(path):
			return
		# the last pixels of the view are reserved for the arrow icon
		if self._xy_is_over_picture(int(event.x), int(event.y)):
			self.emit("user-selected", path, column)
	
	def _on_motion_notify_event(self, widget, event):
		#self.set_has_tooltip(False)
		if self._xy_is_over_picture(int(event.x), (event.y)):
			self.window.set_cursor(self._cursor_hand)
		else:
			self.window.set_cursor(None)
	
	def _xy_is_over_picture(self, x, y):
		if x <= 48:
			return True
		
		return False
	
	def _on_user_selected(self, treeview, path, view_column, *user_params):
		iter = self.get_model().get_iter(eval(path))
		row = self.get_model()[iter]
		
		profile = wndProfile()
		ezGlade.threaded_call(view_profile, (profile, row[3]), breakpoint = (lambda: profile.is_loaded))
		
	def get_model(self):
		return self._store
		
	def append(self, list):
		self._store.append(list)
		
	def prepend(self, list):
		self._store.prepend(list)

class wndCream(ezGlade.BaseWindow):
	"Main form"
	
	timeline = TweetTree()
	replies = TweetTree()
	direct_messages = TweetTree()
	
	timeline_loaded = False
	replies_loaded = False
	dms_loaded = False
	latest_loaded = False
	followers_loaded = False
	account_loaded = False
	first_run = ['timeline', 'mentions', 'direct']
	
	def on_load(self):
		self.set_title(APP_NAME)
		
		settings = ezGlade.AppSettings(APP_NAME)
		settings.read()
		
		show_bar = settings.get_value("Interface", "show_bar", 'True')
		if show_bar == "True":
			show_bar = True
		else:
			show_bar = False
			
		width = int(settings.get_value("Interface", "width", 640))
		height = int(settings.get_value("Interface", "height", 480))
		
		# Sets the value and launches the signal
		self.tbMessage.set_active(show_bar)
		
		self.win.resize(width, height)
		
		#### Columns
		self.swTimeline.add(self.timeline)
		self.swMentions.add(self.replies)
		self.swDMs.add(self.direct_messages)		
		######################

		# Create status icon
		self.icon = gtk.status_icon_new_from_file(os.path.join(PROJECT_PATH, "resources", "twitter.svg"))
		self.icon.connect('activate', self.toggleVisibility)
		self.icon.connect('popup-menu', self.on_right_click)
		self.icon.set_tooltip(APP_NAME)
		
		# Load followers and profile on background
		#ezGlade.threaded_call(self.load_followers, breakpoint = (lambda: self.followers_loaded))
		if USERNAME and PASSWORD:
			ezGlade.threaded_call(self.load_account, breakpoint = (lambda: self.account_loaded))
		
		ezGlade.idle_events()
	
	def toggleVisibility(self, widget):
		if self.isVisible():
			self.hide()
		else:
			self.show()
			self.win.show_all()
	
	def on_right_click(self, icon, event_button, event_time):
		mPopup = mnuPopup()
		mPopup.win.popup(None, None,
			gtk.status_icon_position_menu, event_button,
			event_time, icon)
	
	def load_account(self):
		self.account_loaded = False
		if not USERNAME or not PASSWORD:
			self.account_loaded = True
			return
		try:
			api = twitter.Api(username=USERNAME, password=PASSWORD)
			user = api.GetUser(USERNAME)
		except Exception, e:
			logging.error(str(e))
			self.account_loaded = True
			return
		#pprint(dir(user))
		
		file_url = tweet_browser.cache_image(user.profile_image_url, CACHE_PATH)
		file_url = file_url.replace("file://", "")
		
		self.imgUser.set_from_file(file_url)
		self.lblScreenName.set_label("<b>"+ user.screen_name + "</b>")
		self.account_loaded = True
		
	def on_btnMarkRead_clicked(self, widget):
		"Gets the top date and marks all behind that as read"
		
		sql = """SELECT max(top_date) FROM (
						SELECT max(date) as top_date from direct
						UNION
						SELECT max(date) as top_date from mention
						UNION
						SELECT max(date) as top_date from tweet)"""
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		cursor = DBCON.cursor()
		
		cursor.execute(sql)
		fecha = None
		try:
			fecha = cursor.fetchone()[0]
		except Exception, e:
			logging.error("No messages found")
			return
		
		cursor.execute("UPDATE tweet set unread = 0 WHERE date <=? and unread = 1", (fecha,) )
		cursor.execute("UPDATE mention set unread = 0 WHERE date <=? and unread = 1", (fecha,) )
		cursor.execute("UPDATE direct set unread = 0 WHERE date <=? and unread = 1", (fecha,) )
		DBCON.commit()
		
		cursor.close()
		DBCON.close()
		
		columns = (self.timeline, self.replies, self.direct_messages)
		
		for column in columns:
			model = column.get_model()
			
			for item in model:
				if item[4] == BG_UNREAD:
					item[4] = BG_READ
	
	
	def on_btnAccount_clicked(self, widget):
		account = wndAccount()
		account.set_modal(True)
		account.set_parent(self)
		account.show()

	def on_txtMessage_changed(self, widget):
		current_text = widget.get_text()
		chars_left = 140 - len(current_text)
		
		self.lblCounter.set_label("<i>%d</i>" % chars_left)
	
	def on_btnRefresh_clicked(self, widget):
		#ezGlade.MessageBox("Refrescar", type="info")
		self.refresh_all()
	
	def refresh_all(self):
		self.load_reset()
		
		# TODO: Change the icon tooltip so that it shows how many pending unread messages there are
		
		ezGlade.threaded_call(self.refresh_latest_message, breakpoint = (lambda: self.latest_loaded))
		
		#self.refresh_timeline(False)
		#, delay = 10
		ezGlade.threaded_call(self.refresh_timeline, breakpoint = (lambda: self.timeline_loaded))
		ezGlade.threaded_call(self.refresh_replies, breakpoint = (lambda: self.replies_loaded))
		ezGlade.threaded_call(self.refresh_dms, breakpoint = (lambda: self.dms_loaded))
	
	def load_reset(self):
		self.timeline_loaded = False
		self.replies_loaded = False
		self.dms_loaded = False
		self.latest_loaded = False
		
	def load_followers(self):
		try:
			api = twitter.Api(username = USERNAME, password = PASSWORD)
			friends = api.GetFollowers()
		except Exception, e:
			logging.error(str(e))
			return
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		cursor = DBCON.cursor()
		
		cursor.execute("DELETE FROM followers")
		
		for friend in friends:
			params = (friend.screen_name, friend.profile_image_url)
			cursor.execute("INSERT INTO followers(username, picture) VALUES(?, ?)", params)
		DBCON.commit()
		
		self.followers_loaded = True
		
		cursor.close()
		DBCON.close()
	
	def refresh_latest_message(self):
		try:
			latest = twitter.Api().GetUserTimeline(user=USERNAME, count=1)[0]
		except Exception, e:
			logging.error(str(e))
			return

		self.lblStatus.set_label("<i>" + latest.text + "</i>")
		self.latest_loaded = True
		
	
	def refresh_timeline(self, load_from_web = True):
		logging.debug("Loading timeline")
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		DBCON.row_factory = sqlite3.Row

		cursor = DBCON.cursor()
		cont = 0
		
		if load_from_web:
			connected = False
			
			cursor.execute("SELECT max(id) FROM tweet")
			try:
				max_id = cursor.fetchone()[0]
			except Exception, e:
				logging.error("Cannot get max id:" + str(e))
				max_id = None
			
			try:
				api = twitter.Api(username=USERNAME, password=PASSWORD)
				my_timeline = api.GetFriendsTimeline(count = 100, since_id = max_id)
				
				connected = True
			except Exception, e:
				self.set_status_message(str(e))
				logging.error(str(e))
				return
			
			if connected:
				for status in my_timeline:
					params = (status.id, status.created_at_in_seconds, status.user.screen_name, status.text, status.user.profile_image_url)
					try:
						cursor.execute("INSERT INTO tweet(id, date, username, message, picture) VALUES (?, ?, ?, ?, ?)", params)
					except Exception, e:
						break
					cont += 1
				DBCON.commit()
		
		model = self.timeline.get_model()
		first_iter = model.get_iter_first()
		
		
		if first_iter:
			max_id = model.get_value(first_iter, 0)
		else:
			max_id = 0
		
		cursor.execute("SELECT id, date, username, message, picture, unread FROM tweet WHERE id>? ORDER BY date DESC LIMIT 200", (max_id,))
		rs_timeline = cursor.fetchall()
		
		# Reverse the indexes to be able to prepend
		reversed_indexes = range(0, len(rs_timeline))
		reversed_indexes.reverse()
		
		for index in reversed_indexes:
			rs = rs_timeline[index]
			
			background = BG_READ
			if rs['unread'] == 1:
				background = BG_UNREAD
			
			pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(tweet_browser.cache_image(rs['picture'], CACHE_PATH, False), 48, 48)
			self.timeline.prepend( [ rs['id'], pixbuf, format_message(rs['username'], rs['message'], rs['date']), rs['username'], background ] )
		
		# Always close cursor and connection
		cursor.close()
		DBCON.close()
		
		#path,column,x,y = self.timeline.get_path_at_pos(0, 0)
		#print type(path)
		
		if self.first_run.count('timeline') == 1:
			self.timeline.scroll_to_cell((0,))
			self.first_run.remove('timeline')
		
		if cont>0:
			NOTIFIER.send(_("New tweets: %d") % cont)
		
		self.timeline_loaded = True
		
	def refresh_replies(self, load_from_web = True):
		logging.debug("Loading Replies")
		cont = 0
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		DBCON.row_factory = sqlite3.Row
		cursor = DBCON.cursor()
			
		if load_from_web:
			connected = False
			cursor.execute("SELECT max(id) FROM mention")
			try:
				max_id = cursor.fetchone()[0]
			except Exception, e:
				logging.error("Cannot get max ID:" + str(e))
				max_id = None
				
			try:
				api = twitter.Api(username=USERNAME, password=PASSWORD)
			
				my_replies = api.GetReplies(since_id = max_id)
				connected = True
			except Exception, e:
				self.set_status_message(str(e))
				logging.error(str(e))
	
			
			if connected:
				for status in my_replies:
					params = (status.id, status.created_at_in_seconds, status.user.screen_name, status.text, status.user.profile_image_url)
					try:
						cursor.execute("INSERT INTO mention(id, date, username, message, picture) VALUES (?, ?, ?, ?, ?)", params)
					except Exception, e:
						break
					cont += 1
				DBCON.commit()
		model = self.replies.get_model()
		first_iter = model.get_iter_first()
		
		if first_iter:
			max_id = model.get_value(first_iter, 0)
		else:
			max_id = 0
			
		cursor.execute("SELECT date, username, message, picture, unread, id FROM mention WHERE id>? ORDER BY date DESC LIMIT 50", (max_id,))
		rs_mentions = cursor.fetchall()
		
		# Reverse the indexes to be able to prepend
		reversed_indexes = range(0, len(rs_mentions))
		reversed_indexes.reverse()
		
		for index in reversed_indexes:
			rs = rs_mentions[index]
			
			background = BG_READ
			if rs['unread'] == 1:
				background = BG_UNREAD
			
			pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(tweet_browser.cache_image(rs['picture'], CACHE_PATH, False), 48, 48)
			self.replies.prepend([ rs['id'], pixbuf, format_message(rs['username'], rs['message'], rs['date']), rs['username'], background ])
		
		# Always close cursor and connection
		cursor.close()
		DBCON.close()

		
		if self.first_run.count('mentions') == 1 :
			self.replies.scroll_to_cell((0,))
			self.first_run.remove('mentions')
		
		if cont>0:
			NOTIFIER.send(_("New mentions: %d") % cont)
			
		self.replies_loaded = True
		
	def refresh_dms(self, load_from_web = True):
		logging.debug("Loading DM's")
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		DBCON.row_factory = sqlite3.Row
		cursor = DBCON.cursor()
		
		cont = 0
		
		if load_from_web:
			connected = False
			cursor.execute("SELECT max(id) FROM direct")
			try:
				max_id = cursor.fetchone()[0]
			except Exception, e:
				logging.error("Cannot get max ID:" + str(e))
				max_id = None
			
			
			try:
				api = twitter.Api(username=USERNAME, password=PASSWORD)
	
				my_dms = api.GetDirectMessages(since_id = max_id)
				connected = True
			except Exception, e:
				self.set_status_message(str(e))
				logging.error(str(e))
				my_dms = None
			
			if connected:
				for status in my_dms:
					try:
						user = api.GetUser(status.sender_id)
					except Exception, e:
						logging.error("User search: " + str(e))
						user = None
						
						# TODO: Until I find a solution I preffer not to save the message until I get a propper user object
						break
					
					params = (status.id, status.created_at_in_seconds, user.screen_name, status.text, user.profile_image_url)
					
					try:
						cursor.execute("INSERT INTO direct(id, date, username, message, picture) VALUES (?, ?, ?, ?, ?)", params)
					except Exception, e:
						break
					cont += 1
				
				DBCON.commit()
		
		model = self.direct_messages.get_model()
		first_iter = model.get_iter_first()
		
		if first_iter:
			max_id = model.get_value(first_iter, 0)
		else:
			max_id = 0
				
		cursor.execute("SELECT date, username, message, picture, unread, id FROM direct WHERE id>? ORDER BY date DESC LIMIT 50", (max_id,))
		rs_direct = cursor.fetchall()
		
		# Reverse the indexes to be able to prepend
		reversed_indexes = range(0, len(rs_direct))
		reversed_indexes.reverse()
		
		for index in reversed_indexes:
			rs = rs_direct[index]
			
			background = BG_READ
			if rs['unread'] == 1:
				background = BG_UNREAD
			
			pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(tweet_browser.cache_image(rs['picture'], CACHE_PATH, False), 48, 48)
			self.direct_messages.prepend([ rs['id'], pixbuf, format_message(rs['username'], rs['message'], rs['date']), rs['username'], background ])
		
		# Always close cursor and connection
		cursor.close()
		DBCON.close()
		
		if self.first_run.count('direct') == 1 :
			self.direct_messages.scroll_to_cell((0,))
			self.first_run.remove('direct')
		

		if cont>0:
			NOTIFIER.send(_("New direct messages: %d") % cont)
		
		self.dms_loaded = True
	
	def set_status_message(self, message, context = "error"):
		context = self.stMain.get_context_id(context)
		self.stMain.push(context, message)
	
	def update_status(self, message):
		self.vboxUpdateCtrl.set_sensitive(False)
		self.btnSend.set_sensitive(False)
		
		try:
			api = twitter.Api(username=USERNAME, password=PASSWORD)
			api.SetSource(APP_NAME)
			
			status = api.PostUpdate(message)
		except Exception, e:
			self.vboxUpdateCtrl.set_sensitive(True)
			self.btnSend.set_sensitive(True)
			logging.error("Error: " + str(e))
			return
		
		self.txtMessage.set_text("")
		self.lblStatus.set_label("<i>%s</i>" % status.text)
		
		self.vboxUpdateCtrl.set_sensitive(True)
		self.btnSend.set_sensitive(True)
		
	def on_btnDM_clicked(self, widget):
		send_dm = wndDM()
		send_dm.set_parent(self)
		send_dm.set_modal(True)
		send_dm.show()
	
	def on_btnSend_clicked(self, widget):
		logging.debug("Sending message")
		
		message = self.txtMessage.get_text()
		ezGlade.do_gui_operation(self.update_status, message)
	
	def on_tbMessage_toggled(self, widget):
		active = widget.get_active()
		
		if not active:
			self.vboxUpdateCtrl.hide()
		else:
			self.vboxUpdateCtrl.show()
	
	def on_txtMessage_activate(self, widget):
		self.on_btnSend_clicked(widget)
	
	def on_btnExit_clicked(self, widget):
		self.exit()
	
	def destroy(self, widget):
		self.hide()
		
	def exit(self):
		show_hide = self.tbMessage.get_active()
		size = self.win.get_size()
		
		## Save some preferences
		settings = ezGlade.AppSettings(APP_NAME)
		settings.read()
		settings.set_value("Interface", "show_bar", show_hide)
		settings.set_value("Interface", "width", size[0])
		settings.set_value("Interface", "height", size[1])
		settings.save()
		
		ezGlade.quit()
		

class wndAccount(ezGlade.BaseWindow):
	validated = False
	
	def on_load(self):
		settings = ezGlade.AppSettings(APP_NAME)
		settings.read()
		self.enUsername.set_text( settings.get_value("General", "username", "") )
		self.enPassword.set_text( settings.get_value("General", "password", "") )
		self.enUsername.grab_focus()
		
	def is_validated(self):
		return self.validated
	
	def validate_account(self, username, password):
		followers = None
		
		try:
			api = twitter.Api(username, password)
			followers = api.GetFollowers()
		except Exception, e:
			logging.error(str(e))
			self.validated = False
			return
		
		self.validated = True
			
	def on_btnOk_clicked(self, widget):
		global USERNAME, PASSWORD
		
		settings = ezGlade.AppSettings(APP_NAME)
		settings.read()
		
		username = self.enUsername.get_text()
		password = self.enPassword.get_text()
		
		self.lblStatus.set_text(_("Please wait a moment while \nyour account is being validated"))
		self.lblStatus.show()
		
		ezGlade.idle_events()
		
		self.validate_account(username, password)
		
		if not self.validated:
			self.lblStatus.set_text(_("Cannot validate the specified user"))
		else:
			settings.set_value("General", "username", username)
			settings.set_value("General", "password", password)
			
			USERNAME = username
			PASSWORD = password
		
			settings.save()
			self.parent.load_account()
			self.parent.refresh_all()
			
			self.destroy()
	
	def on_enUsername_activate(self, widget):
		self.enPassword.grab_focus()
		
	
	def on_enPassword_activate(self, widget):
		self.on_btnOk_clicked(widget)
	
	def on_btnCancel_clicked(self, widget):
		self.destroy()

class wndDM(ezGlade.BaseWindow):
	"Window used to send Direct messages"
	
	def on_load(self):
		entries = gtk.EntryCompletion()
		model = gtk.ListStore(str)
		
		entries.set_model(model)
		entries.set_text_column(0)
		
		self.enUsername.set_completion(entries)
		
		DBCON = sqlite3.connect(DB_PATH, detect_types = sqlite3.PARSE_COLNAMES)
		cursor = DBCON.cursor()
		cursor.execute("SELECT username FROM followers")
		
		for friend in cursor.fetchall():
			model.append([friend[0]])
			
		cursor.close()
		DBCON.close()
	
	def on_btnOk_clicked(self, widget):
		username = self.enUsername.get_text()

		text = self.txtMessage.get_text()
		
		try:
			api = twitter.Api(USERNAME, PASSWORD)
			api.SetSource(APP_NAME)
			api.PostDirectMessage(username, text)
		except Exception, e:
			logging.error("Cannot send DM: " + str(e))
			return
		
		self.destroy()
	
	def on_txtMessage_changed(self, widget):
		text = self.txtMessage.get_text()
		
		count = 140 - len(text)
		self.lblCounter.set_label("<i>%d</i>" % count)

	def on_btnCancel_clicked(self, widget):
		self.destroy()

class wndProfile(ezGlade.BaseWindow):
	username = None
	is_loaded = False
	
	def on_load(self):
		pass
	
	def set_user(self, user):
		self.set_title(user + _(" profile"))
		self.username = user
	
		if self.username:
			connected = False
			
			try:
				api = twitter.Api(username=USERNAME, password=PASSWORD)
			
				user = api.GetUser(self.username)
				connected = True
			except Exception, e:
				logging.error(str(e))
				return
				
			self.lblUser.set_label("<b>%s</b> - (aka: %s)" % (user.name, user.screen_name))
			if user.description:
				self.lblData.set_text(user.description)
				
			if user.location:
				self.lblDataLocation.set_text(user.location)
				
			if user.status:
				self.lblDataUpdate.set_text(user.status.text)
			
			img_path = tweet_browser.cache_image(user.profile_image_url.replace("_normal", "_bigger"), CACHE_PATH).replace("file://","")
			
			self.imgUser.set_from_file(img_path)
			if user.url:
				self.btnUrl.show()
				self.btnUrl.set_label( user.screen_name + _("'s homepage"))
				self.btnUrl.set_uri(user.url)
			
		self.is_loaded = True
	
	def on_btnClose_clicked(self, widget):
		self.destroy()

class Timer(Thread):
	action = None
	sleeptime = 60
	
	def __init__(self, action, sleeptime):
		 Thread.__init__(self)
		 
		 self.action = action
		 self.sleeptime=sleeptime
	
	def run(self):
		if self.action:
			while True:
				ezGlade.do_gui_operation(self.action)
				time.sleep(self.sleeptime)
			
class Notifier:
	notify_window = None
	uri = "file://" + os.path.join(PROJECT_PATH, "resources", "twitter.svg")
	
	def __init__(self):
	
		# Initializes notification service
		pynotify.init(APP_NAME)
		
		self.notify_window = pynotify.Notification(APP_NAME, " ", self.uri)
	
	def send(self, message):
		"Uses the notification library to show a simple message"
		
		if not message:
			return
		
		if self.notify_window.props.id!=0:
			message = self.notify_window.props.body + (" "*50) + ' %s' % message
		
		self.notify_window.update(APP_NAME, message, self.uri)
		
		if not self.notify_window.show():
			logging.error(_("Failed to send notification"))


if __name__ == '__main__':
	if not os.path.exists(HOME_PATH):
		os.mkdir(HOME_PATH)
		
	if not os.path.exists(CACHE_PATH):
		os.mkdir(CACHE_PATH)
	
	logging_file = os.path.join(HOME_PATH, 'errors.log')

	## Set's up the logging format
	logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', filename = logging_file)
	
	## Defines wich glade file is going to be used
	ezGlade.set_file(os.path.join(PROJECT_PATH, "interface.glade"))
	
	## Reads the settings file and makes sure there is a username and password set
	settings = ezGlade.AppSettings(APP_NAME)
	settings.read()
	USERNAME = settings.get_value("General", "username")
	PASSWORD = settings.get_value("General", "password")
	SLEEP_TIME = settings.get_value("General", "interval", 60)
	
	DB_PATH = os.path.join(HOME_PATH, 'cache.db')
	db_exists = False
	
	if os.path.exists(DB_PATH):
		db_exists = True
	
	# Create database and new tables
	DBCON = sqlite3.connect(DB_PATH)
	cursor = DBCON.cursor()

	cursor.execute('''CREATE TABLE IF NOT EXISTS "tweet" (
				  "id" INTEGER PRIMARY KEY  NOT NULL,
				  "date" DOUBLE NOT NULL,
				  "username" TEXT NOT NULL,
				  "message" TEXT NOT NULL,
				  "picture" TEXT NOT NULL ,
				  "unread" INTEGER NOT NULL  DEFAULT 1)''')
				  
	cursor.execute('''CREATE TABLE IF NOT EXISTS "mention" (
				  "id" INTEGER PRIMARY KEY  NOT NULL,
				  "date" DOUBLE NOT NULL,
				  "username" TEXT NOT NULL,
				  "message" TEXT NOT NULL,
				  "picture" TEXT NOT NULL,
				  "unread" INTEGER NOT NULL  DEFAULT 1)''')
				  
	cursor.execute('''CREATE TABLE IF NOT EXISTS "direct" (
				  "id" INTEGER PRIMARY KEY  NOT NULL,
				  "date" DOUBLE NOT NULL,
				  "username" TEXT NOT NULL,
				  "message" TEXT NOT NULL,
				  "picture" TEXT NOT NULL,
				  "unread" INTEGER NOT NULL  DEFAULT 1);
				  ''')
	
	cursor.execute('''CREATE TABLE IF NOT EXISTS "followers" (
				   "username" VARCHAR PRIMARY KEY  NOT NULL ,
				   "picture" TEXT NOT NULL )''')
	
	DBCON.commit()
	DBCON.close()
	
	mainWindow = wndCream()
	mainWindow.show()

	# Sets up the timer to refresh every XX seconds
	# TODO: Make the interval configurable from within the GUI
	timer = Timer(mainWindow.refresh_all, int(SLEEP_TIME))
	timer.start()
	
	if not USERNAME or not PASSWORD:
		account = wndAccount()
		account.set_modal(True)
		account.set_parent(mainWindow)
		account.show()

	NOTIFIER = Notifier()
	
	ezGlade.run()
	
